package asteroids.model.programs.statements.ProgramStatements;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import asteroids.model.SpaceObject;
import asteroids.model.World;
import asteroids.model.programs.Program;
import asteroids.model.programs.expressions.Expression;
import asteroids.model.programs.expressions.BasicExpressions.Entity;
import asteroids.model.programs.parsing.ProgramFactory.ForeachType;
import asteroids.model.programs.statements.ActionStatement;
import asteroids.model.programs.statements.Statement;
import asteroids.model.programs.types.Type;

public class ForEachStatement extends Statement {

	public ForEachStatement(int line, int column, ForeachType type, String variableName, Statement statement) {
		super(line, column);
		this.statement = statement;
		this.type = type;
		this.variableName = variableName;
		this.linkMap = new HashMap<ForeachType, Set<? extends SpaceObject>>();
		
	}

	@Override
	public void execute() {
		this.init();
		
		Set<? extends SpaceObject> set = linkMap.get(this.type);
		for(SpaceObject so: set) {
			Entity spaceEntity = new Entity(so, this.getLine(), this.getColumn());
			this.getProgram().setVariableReference(variableName, spaceEntity);
			statement.execute();
		}
		
	}
	
	private void init() {
		World world = this.getProgram().getSelf().getWorld();
		linkMap.put(ForeachType.SHIP, world.getShips());
		linkMap.put(ForeachType.ASTEROID, world.getAsteroids());
		linkMap.put(ForeachType.ANY, world.getAll());
		linkMap.put(ForeachType.BULLET, world.getBullets());
	}
	
	private HashMap<ForeachType, Set<? extends SpaceObject>> linkMap;

	@Override
	public void setProgram(Program program) {
		super.setProgram(program);
		statement.setProgram(program);
	}

	public void setStatement(Statement statement) {
		this.statement = statement;
	}

	public Statement getStatement() {
		return this.statement;
	}

	private Statement statement;

	private final ForeachType type;

	@Override
	public Expression getExpression() {
		return null;
	}
	
	private String variableName;

	@Override
	public String typeCheckStatement(String errorMessage) {
		String headerErrorMessage = null;
		if(getProgram().getGlobals().get(variableName) == null) {
			headerErrorMessage = "The variable " + variableName + " called in the "
								+ " for each statement at line " + getLine() + " and column " 
								+ getColumn() + " is not a global variable\n";
		} else if(getProgram().getGlobals().get(variableName) != Type.ENTITY){
			headerErrorMessage = "Cannot convert from " + type + " to " + getProgram().getGlobals().get(variableName)
								 + " at line " + getLine() + " and column " + getColumn()+ "\n";
		}
		
		String bodyErrorMessage = getStatement().typeCheckStatement(headerErrorMessage);
		
		String actionErrorMessage = "The foreach loop at line " + getLine() + " and column " 
								+ getColumn() + " should not contain an action statement\n";
		ArrayList<Statement> arrayStatements = ((SequenceStatement)getStatement()).convertToArrayList();
		boolean containsActionStatement = false;
		for(Statement checkedStatement : arrayStatements){
			if(checkedStatement instanceof ActionStatement){
				containsActionStatement = true;
				break;
			}
		}
		
		if(containsActionStatement){
			if(bodyErrorMessage == null) bodyErrorMessage = actionErrorMessage;
			else bodyErrorMessage += actionErrorMessage;
		}
		return bodyErrorMessage;
	}

}
